﻿
using System;
using System.Collections.Generic;
using Test30.Core;

namespace Test30.Engine
{
    public struct Direction : IEquatable<Direction>
    {
        public static Direction None => new Direction(Vec.Zero);

        public static Direction N => new Direction(new Vec(0, 1));
        public static Direction Ne => new Direction(new Vec(1, 1));
        public static Direction E => new Direction(new Vec(1, 0));
        public static Direction Se => new Direction(new Vec(1, -1));
        public static Direction S => new Direction(new Vec(0, -1));
        public static Direction Sw => new Direction(new Vec(-1, -1));
        public static Direction W => new Direction(new Vec(-1, 0));
        public static Direction Nw => new Direction(new Vec(-1, 1));

        public static Vec operator +(Vec v1, Direction d2)
        {
            return v1 + d2.Offset;
        }
        public static Vec operator +(Direction d1, Vec v2)
        {
            return d1.Offset + v2;
        }
        public static IList<Direction> Clockwise => new List<Direction> { N, Ne, E, Se, S, Sw, W, Nw };

        public static IList<Direction> Counterclockwise => new List<Direction> { N, Nw, W, Sw, S, Se, E, Ne };

        public static IList<Direction> Nsew => new List<Direction> { N, S, E, W };

        public static Direction Towards(Vec pos)
        {
            Vec offset = Vec.Zero;

            if (pos.X < 0) offset.X = -1;
            if (pos.X > 0) offset.X = 1;
            if (pos.Y < 0) offset.Y = -1;
            if (pos.Y > 0) offset.Y = 1;

            return new Direction(offset);
        }
        public static bool operator ==(Direction left, Direction right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(Direction left, Direction right)
        {
            return !left.Equals(right);
        }
        public Vec Offset => _offset;

        public Direction Next
        {
            get
            {
                if (this == N) return Ne;
                else if (this == Ne) return E;
                else if (this == E) return Se;
                else if (this == Se) return S;
                else if (this == S) return Sw;
                else if (this == Sw) return W;
                else if (this == W) return Nw;
                else if (this == Nw) return N;
                else return None;
            }
        }
        public Direction Previous
        {
            get
            {
                if (this == N) return Nw;
                else if (this == Ne) return N;
                else if (this == E) return Ne;
                else if (this == Se) return E;
                else if (this == S) return Se;
                else if (this == Sw) return S;
                else if (this == W) return Sw;
                else if (this == Nw) return W;
                else return None;
            }
        }
        public Direction RotateLeft90 => Previous.Previous;

        public Direction RotateRight90 => Next.Next;

        public Direction Rotate180 => new Direction(_offset * -1);

        public override bool Equals(object obj)
        {
            // check the type
            if (!(obj is Direction)) return false;

            // call the typed Equals
            return Equals((Direction)obj);
        }

        public override int GetHashCode()
        {
            return Offset.GetHashCode();
        }

        public override string ToString()
        {
            if (this == N) return "N";
            else if (this == Ne) return "NE";
            else if (this == E) return "E";
            else if (this == Se) return "SE";
            else if (this == S) return "S";
            else if (this == Sw) return "SW";
            else if (this == W) return "W";
            else if (this == Nw) return "NW";
            else if (this == None) return "None";

            return Offset.ToString();
        }

        private Direction(Vec offset)
        {
            _offset = offset;
        }

        #region IEquatable<Direction> Members

        public bool Equals(Direction other)
        {
            return Offset.Equals(other.Offset);
        }

        #endregion

        private readonly Vec _offset;
    }
}
